
package com.googlecode.esblabs.util.mule.exception;

import java.io.StringWriter;

import org.mule.api.MuleMessage;
import org.mule.api.service.ServiceException;
import org.mule.api.transformer.TransformerException;
import org.mule.message.ExceptionMessage;
import org.mule.transformer.AbstractMessageAwareTransformer;

import com.googlecode.esblabs.cmf.ErrorInformationDataAreaType;
import com.googlecode.esblabs.cmf.ErrorInformationType;
import com.googlecode.esblabs.cmf.ExceptionType;
import com.googlecode.esblabs.cmf.ErrorInformationDataAreaType.Properties;
import com.googlecode.esblabs.util.Util;
import com.googlecode.esblabs.util.mule.transformer.jaxb.XmlBinding;

public class ExceptionToJaxbObject extends AbstractMessageAwareTransformer
{
    public ExceptionToJaxbObject()
    {
        registerSourceType(Object.class);
        setReturnClass(Object.class);
    }

    public Object transform(MuleMessage message, String outputEncoding) throws TransformerException
    {
        try
        {
	        Object               src    = message.getPayload();
	        ErrorInformationType outErr = new ErrorInformationType();
	
	        // Special handling if message is not an ExceptionMessage
			if (!(src instanceof ExceptionMessage)) {
		        outErr = createUnknownExceptionMessage(message);
	
			} else {
	
		        ExceptionMessage exMsg = (ExceptionMessage)src;			
	
		        // Create the out error info msg
		        ErrorInformationDataAreaType outEda = new ErrorInformationDataAreaType();
		        outErr.setDataArea(outEda);
	
	
		        // Fill on basic error info
		        outEda.setComponent(exMsg.getComponentName());
		        outEda.setEndpoint(exMsg.getEndpoint().toString());
		        outEda.setTimestamp(Util.convertDateTimeToXMLGregorianCalendar(exMsg.getTimeStamp()));
		        outEda.setPayload(exMsg.getPayload().toString());

		        for (Object k : exMsg.getProperties().keySet()) {
					Object     v = exMsg.getProperties().get(k);
					Properties p = new Properties();
					p.setKey(k.toString());
					p.setValue(v.toString());
					outEda.getProperties().add(p);
				}
	
		        // Fill on with exception info
		        Throwable ex = exMsg.getException();
				
				ExceptionType outEx = new ExceptionType();
				outEx.setExceptionClass(ex.getClass().getName());
				outEx.setMessage(ex.getMessage());
				outEx.setStacktrace('\n' + Util.getStacktrace(ex));
	
				outEda.setException(outEx);
	
				if (ex instanceof ServiceException) {
					ServiceException serviceEx = (ServiceException) ex;
					Throwable causeEx = serviceEx.getCause();
					
					ExceptionType outCauseEx = new ExceptionType();
					outCauseEx.setExceptionClass(causeEx.getClass().getName());
					outCauseEx.setMessage(causeEx.getMessage());
					outCauseEx.setStacktrace('\n' + Util.getStacktrace(causeEx));
		
					outEda.setException(outCauseEx);
				}
			}
	
	    	// check if payload is not a string then try to unmarshall to string
    	
//            Marshaller unmarshaller = jaxbContext.createMarshaller();
//            StringWriter writer = new StringWriter();
//            unmarshaller.marshal(src, writer);
//
//            if (logger.isDebugEnabled()) logger.debug("transform(" + src.getClass().getSimpleName() + ", " + outputEncoding + ") returns: " +  writer.toString());
//            
//            return writer.toString();

//			if (logger.isDebugEnabled()) logger.debug("transform(" + src.getClass().getSimpleName() + ", " + outputEncoding + ") returns: " + outErr);

            StringWriter writer = new StringWriter();
            XmlBinding.marshal(outErr, writer);
        	logger.error('\n' + writer.toString());
        	return outErr;
        }
        catch (Exception e)
        {
            throw new TransformerException(this, e);
        }

    }

	private ErrorInformationType createUnknownExceptionMessage(MuleMessage message) {
		try {
			ErrorInformationType outErr = new ErrorInformationType();
			ErrorInformationDataAreaType outEda = new ErrorInformationDataAreaType();
			outErr.setDataArea(outEda);

			outEda.setComponent("UNKNOWN");
			outEda.setEndpoint("UNKNOWN");
			outEda.setTimestamp(Util.getCurrentDateTime());
			outEda.setPayload(message.getPayloadAsString());
			
			return outErr;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}
